<template>
  <div class="app-container">
    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
      <el-form-item label="系统模块" prop="title">
        <el-input v-model="queryParams.title" placeholder="请输入系统模块" clearable style="width: 240px;"
          @keyup.enter.native="handleQuery" />
      </el-form-item>
      <el-form-item label="操作人员" prop="operName">
        <el-input v-model="queryParams.operName" placeholder="请输入操作人员" clearable style="width: 240px;"
          @keyup.enter.native="handleQuery" />
      </el-form-item>
      <el-form-item label="类型" prop="businessType">
        <el-select v-model="queryParams.businessType" placeholder="操作类型" clearable style="width: 240px">
          <el-option v-for="dict in dict.type.sys_oper_type" :key="dict.value" :label="dict.label"
            :value="dict.value" />
        </el-select>
      </el-form-item>
      <el-form-item label="状态" prop="status">
        <el-select v-model="queryParams.status" placeholder="操作状态" clearable style="width: 240px">
          <el-option v-for="dict in dict.type.sys_common_status" :key="dict.value" :label="dict.label"
            :value="dict.value" />
        </el-select>
      </el-form-item>
      <el-form-item label="操作时间">
        <el-date-picker v-model="dateRange" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
          range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
          :default-time="['00:00:00', '23:59:59']"></el-date-picker>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>

    <el-row :gutter="10" class="mb8">
      <!-- <el-col :span="1.5">
        <el-button
          type="danger"
          plain
          icon="el-icon-delete"
          size="mini"
          :disabled="multiple"
          @click="handleDelete"
          v-hasPermi="['monitor:operlog:remove']"
        >删除</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="danger"
          plain
          icon="el-icon-delete"
          size="mini"
          @click="handleClean"
          v-hasPermi="['monitor:operlog:remove']"
        >清空</el-button>
      </el-col> -->
      <el-col :span="1.5">
        <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
          v-hasPermi="['monitor:operlog:export']">导出
        </el-button>
      </el-col>
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>

    <el-table ref="tables" v-loading="loading" :data="list" @selection-change="handleSelectionChange"
      :default-sort="defaultSort" @sort-change="handleSortChange">
      <el-table-column type="selection" width="50" align="center" />
      <el-table-column label="日志编号" align="center" prop="operId" />
      <el-table-column label="系统模块" align="center" prop="title" :show-overflow-tooltip="true" />
      <el-table-column label="操作类型" align="center" prop="businessType">
        <template slot-scope="scope">
          <dict-tag :options="dict.type.sys_oper_type" :value="scope.row.businessType" />
        </template>
      </el-table-column>
      <el-table-column label="操作人员" align="center" prop="operName" width="110" :show-overflow-tooltip="true"
        sortable="custom" :sort-orders="['descending', 'ascending']" />
      <el-table-column label="操作地址" align="center" prop="operIp" width="130" :show-overflow-tooltip="true" />
      <el-table-column label="操作地点" align="center" prop="operLocation" :show-overflow-tooltip="true" />
      <el-table-column label="操作状态" align="center" prop="status">
        <template slot-scope="scope">
          <dict-tag :options="dict.type.sys_common_status" :value="scope.row.status" />
        </template>
      </el-table-column>
      <el-table-column label="操作日期" align="center" prop="operTime" width="160" sortable="custom"
        :sort-orders="['descending', 'ascending']">
        <template slot-scope="scope">
          <span>{{ parseTime(scope.row.operTime) }}</span>
        </template>
      </el-table-column>
      <el-table-column label="消耗时间" align="center" prop="costTime" width="110" :show-overflow-tooltip="true"
        sortable="custom" :sort-orders="['descending', 'ascending']">
        <template slot-scope="scope">
          <span>{{ scope.row.costTime }}毫秒</span>
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
        <template slot-scope="scope">
          <el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row, scope.index)"
            v-hasPermi="['monitor:operlog:query']">详细
          </el-button>
        </template>
      </el-table-column>
    </el-table>

    <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
      @pagination="getList" />

    <!-- 操作日志详细 -->
    <el-dialog title="操作日志详细" :visible.sync="open" width="1000px" append-to-body>
      <div class="operation-detail-container">
        <!-- 固定基础信息 -->
        <div class="fixed-base-info">
          <el-descriptions :column="3" border>
            <el-descriptions-item label="操作人">
              <span class="highlight-text">{{ baseInfo.operator }}</span>
            </el-descriptions-item>
            <el-descriptions-item label="操作模块">
              {{ baseInfo.module }}
            </el-descriptions-item>
            <el-descriptions-item label="操作时间">
              {{ baseInfo.operTime }}
            </el-descriptions-item>
            <el-descriptions-item label="操作描述">
              <span>{{ baseInfo.operDesc }}</span>
            </el-descriptions-item>
            <el-descriptions-item label="异常信息">
              <span>{{ baseInfo.errorMsg }}</span>
            </el-descriptions-item>
          </el-descriptions>
        </div>

        <!-- 单据头变更 -->
        <div class="change-section">
          <h3 class="section-title">
            <el-icon>
              <document />
            </el-icon>
            主表变更
          </h3>
          <el-table :data="this.getHeaderChanges(this.data.operationDetail)" border size="small"
            empty-text="操作未产生数据变更或不涉及单据头数据">
            <el-table-column prop="field" label="变更字段" width="200">
              <template slot-scope="{ row }">
                <span class="field-name">{{ row.field }}</span>
              </template>
            </el-table-column>
            <el-table-column label="变更前值" width="240">
              <template slot-scope="{ row }">
                <span class="field-name">{{ row.oldValue || '-' }}</span>
              </template>
            </el-table-column>
            <el-table-column label="变更后值" width="240">
              <template slot-scope="{ row }">
                <span class="field-name">{{ row.newValue || '-' }}</span>
              </template>
            </el-table-column>
            <el-table-column label="变更类型">
              <template slot-scope="{ row }">
                <el-tag size="small">{{ row.changeType }}</el-tag>
              </template>
            </el-table-column>
          </el-table>
        </div>

        <!-- 明细行变更 -->
        <div class="change-section">
          <h3 class="section-title">
            <el-icon>
              <list />
            </el-icon>
            子表变更
          </h3>
          <!-- 判断是否有数据 -->
          <template v-if="getDetailChanges(this.data.detailDesc).length > 0">
            <el-collapse accordion>
              <el-collapse-item v-for="(line, index) in getDetailChanges(this.data.detailDesc)" :key="index"
                :name="line.lineNumber">
                <template #title>
                  <!-- <span class="line-title">行号: {{ line.lineNumber }}</span> -->
                  <el-tag size="small">{{ line.changes.length }}项变更</el-tag>
                </template>
                <el-table :data="line.changes" border size="small" empty-text="未检测到变更">
                  <el-table-column prop="field" label="变更字段" width="200">
                    <template slot-scope="{ row }">
                      <span class="field-name">{{ row.field }}</span>
                    </template>
                  </el-table-column>
                  <el-table-column label="变更前值" width="240">
                    <template slot-scope="{ row }">
                      <span class="field-name">{{ row.oldValue || '-' }}</span>
                    </template>
                  </el-table-column>
                  <el-table-column label="变更后值" width="240">
                    <template slot-scope="{ row }">
                      <span class="field-name">{{ row.newValue || '-' }}</span>
                    </template>
                  </el-table-column>
                  <el-table-column label="变更类型">
                    <template slot-scope="{ row }">
                      <el-tag size="small">{{ row.changeType }}</el-tag>
                    </template>
                  </el-table-column>
                </el-table>
              </el-collapse-item>
            </el-collapse>
          </template>
          <!-- 没有数据时显示提示 -->
          <template v-else>
            <el-alert type="info" :closable="false" description="操作未产生数据变更或不涉及明细数据" show-icon />
          </template>
        </div>
      </div>
      <div slot="footer" class="dialog-footer">
        <el-button @click="open = false">关 闭</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
  import {
    list,
    delOperlog,
    cleanOperlog
  } from "@/api/monitor/basicLog";

  // 查询操作日志详情
  import {
    listId
  } from "@/api/monitor/basicLog";

  // 浏览器中对网页进行截图，将这些截图转换为图像格式
  import html2canvas from 'html2canvas';
  // 生成 PDF 的库
  import jsPDF from 'jspdf';
  import 'jspdf-autotable';

  export default {
    name: "Operlog",
    dicts: ['sys_oper_type', 'sys_common_status'],
    data() {
      return {
        // 遮罩层
        loading: true,
        // 选中数组
        ids: [],
        // 非多个禁用
        multiple: true,
        // 显示搜索条件
        showSearch: true,
        // 总条数
        total: 0,
        // 表格数据
        list: [],
        // 是否显示弹出层
        open: false,
        // 日期范围
        dateRange: [],
        // 默认排序
        defaultSort: {
          prop: 'operTime',
          order: 'descending'
        },
        // 表单参数
        form: {},
        // 接收详情请求到数据
        data: {},
        // 详情显示基础数据
        baseInfo: {
          operator: '',
          operTime: '',
          method: '',
          apiPath: '',
          cost: '',
          operDesc: '' // 新增操作描述字段
        },
        // 查询参数
        queryParams: {
          pageNum: 1,
          pageSize: 10,
          title: undefined,
          operName: undefined,
          businessType: undefined,
          status: undefined
        }
      };
    },
    created() {
      this.getList();
    },
    methods: {
      /** 查询登录日志 */
      getList() {
        this.loading = true;
        this.open = false; // 确保对话框关闭
        list(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
          this.list = response.rows;
          this.total = response.total;
          this.loading = false;
        });
      },

      // 操作日志类型字典翻译
      typeFormat(row, column) {
        return this.selectDictLabel(this.dict.type.sys_oper_type, row.businessType);
      },

      /** 搜索按钮操作 */
      handleQuery() {
        this.queryParams.pageNum = 1;
        this.getList();
      },

      /** 重置按钮操作 */
      resetQuery() {
        this.dateRange = [];
        this.resetForm("queryForm");
        this.queryParams.pageNum = 1;
        this.$refs.tables.sort(this.defaultSort.prop, this.defaultSort.order)
      },

      /** 多选框选中数据 */
      handleSelectionChange(selection) {
        this.ids = selection.map(item => item.operId)
        this.multiple = !selection.length
      },

      /** 排序触发事件 */
      handleSortChange(column, prop, order) {
        this.queryParams.orderByColumn = column.prop;
        this.queryParams.isAsc = column.order;
        this.getList();
      },

      /** 详细按钮操作 */
      handleView(row) {
        this.open = true;
        // this.form = row;

        let id = row.operId;

        this.loading = true;
        listId(id).then(response => {
          if (response && response.data) {
            this.data = response.data;
            console.log("listId=", response.data)
            this.parseBasicInfo();
          } else {
            console.error('响应数据结构不符合预期:', response);
          }
          this.loading = false;
        }).catch(error => {
          console.error('请求出错:', error);
          this.loading = false;
        });
      },

      /** 删除按钮操作 */
      handleDelete(row) {
        const operIds = row.operId || this.ids;
        this.$modal.confirm('是否确认删除日志编号为"' + operIds + '"的数据项？').then(function() {
          return delOperlog(operIds);
        }).then(() => {
          this.getList();
          this.$modal.msgSuccess("删除成功");
        }).catch(() => {});
      },

      /** 清空按钮操作 */
      handleClean() {
        this.$modal.confirm('是否确认清空所有操作日志数据项？').then(function() {
          return cleanOperlog();
        }).then(() => {
          this.getList();
          this.$modal.msgSuccess("清空成功");
        }).catch(() => {});
      },
      /** 导出按钮操作 */
      // handleExport() {
      //   this.download('monitor/operlog/export', {
      //     ...this.queryParams
      //   }, `operlog_${new Date().getTime()}.xlsx`)
      // }

      /** 导出按钮操作(初步2，成功导出PDF，导出内容加10px边框) */
      // async handleExport() {
      //   const table = this.$refs.tables.$el;
      //   const canvas = await html2canvas(table);
      //   const imgData = canvas.toDataURL('image/png');

      //   const doc = new jsPDF();
      //   const imgProps = doc.getImageProperties(imgData);
      //   const pdfWidth = doc.internal.pageSize.getWidth();
      //   const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;

      //   // 添加 10px 的边距
      //   const margin = 10;
      //   const x = margin;
      //   const y = margin;
      //   const newWidth = pdfWidth - 2 * margin;
      //   const newHeight = (imgProps.height * newWidth) / imgProps.width;

      //   doc.addImage(imgData, 'PNG', x, y, newWidth, newHeight);
      //   doc.save(`logininfor_${new Date().getTime()}.pdf`);
      // },
      /** 导出按钮操作 */
      handleExport() {
        this.download('monitor/basic/operlog/export', {
          ...this.queryParams
        }, `基础操作日志_${this.formatTime(new Date())}.pdf`)
      },

      // 核心解析方法
      parseChangeData(rawData) {
        try {
          const data = typeof rawData === 'string' ? JSON.parse(rawData) : rawData;
          return Object.entries(data).map(([key, value]) => {
            // 判断是否为日期字段
            const isDateField = (field) => field.includes('日期') || field.includes('时间');

            // 日期格式化处理（移除末尾的.0）
            const formatDate = (val) => {
              if (typeof val === 'string' && isDateField(key)) {
                return val.replace(/\.\d+$/, ''); // 移除末尾的.和数字
              }
              return val;
            };

            // 提取并格式化新旧值
            const oldVal = value.oldValue !== undefined ? formatDate(value.oldValue) : null;
            const newVal = value.newValue !== undefined ? formatDate(value.newValue) : null;

            // 判断变更类型
            let changeType = '数值变更';
            if (value.oldValue === undefined || value.oldValue === null) {
              changeType = '新增字段';
            } else if (value.newValue === undefined || value.newValue === null) {
              changeType = '删除字段';
            }

            return {
              field: this.mapFieldName(key), // 保留字段名映射逻辑
              oldValue: oldVal,
              newValue: newVal,
              changeType: changeType
            };
          });
        } catch (e) {
          return [];
        }
      },

      // 解析详情显示基础信息
      parseBasicInfo() {
        this.baseInfo = {
          operator: this.data.operName || '未知',
          operTime: this.formatTime(this.data.operTime),
          method: this.data.requestMethod || '-',
          apiPath: this.data.operUrl || '-',
          cost: `${this.data.costTime}ms`,
          operDesc: this.data.operDesc || '无描述',
          errorMsg: this.data.errorMsg || '无错误信息', // 新增操作描述解析
          module: this.data.title + "/" + this.typeFormat(this.data)
        }
      },

      // 解析表头变更数据
      getHeaderChanges(rawData) {
        try {
          if (rawData == null || rawData === '') {
            return [];
          }
          const data = typeof rawData === 'string' ? JSON.parse(rawData) : rawData;
          const headerChanges = [];

          Object.entries(data).forEach(([key, value]) => {
            // 忽略行号数据（明细数据）
            if (key.startsWith('行号') || key.includes('行号：')) return;

            // 处理表头字段
            const change = this.processField(key, value);
            headerChanges.push(change);
          });

          return headerChanges;
        } catch (e) {
          console.error('表头数据解析错误:', e);
          return [];
        }
      },

      // 解析明细数据 - 优化版本
      getDetailChanges(detailDesc) {
        if (!detailDesc || !Array.isArray(detailDesc)) {
          return [];
        }

        const lineChanges = [];

        detailDesc.forEach((item, index) => {
          try {
            // 解析JSON字符串
            const data = typeof item === 'string' ? JSON.parse(item) : item;

            // 处理每个字段的变更
            const changes = Object.entries(data).map(([fieldName, fieldValue]) => {
              return {
                field: this.mapFieldName(fieldName),
                oldValue: fieldValue.oldValue ?? '-',
                newValue: fieldValue.newValue ?? '-',
                changeType: this.determineChangeType(fieldValue)
              };
            });

            // 如果有变更才添加到结果
            if (changes.length > 0) {
              lineChanges.push({
                lineNumber: this.extractLineNumber(data) || `变更${index + 1}`,
                changes: changes
              });
            }
          } catch (e) {
            console.error('解析明细数据出错:', e);
          }
        });

        return lineChanges;
      },

      // 提取行号
      extractLineNumber(data) {
        const lineKey = Object.keys(data).find(key => key.includes('行号'));
        if (!lineKey) return null;

        const match = lineKey.match(/行号[:：]?(\d+)/);
        return match ? match[1] : null;
      },

      // 判断变更类型
      determineChangeType(value) {
        if (value.oldValue == null) return '新增字段';
        if (value.newValue == null) return '删除字段';
        return '数值变更';
      },
      // 通用字段处理逻辑
      processField(key, value) {
        const isDateField = (k) => k.includes('日期') || k.includes('时间');
        const formatValue = (val, k) =>
          typeof val === 'string' && isDateField(k) ? val.replace(/\.\d+$/, '') : val;

        // 判断变更类型
        let changeType = '数值变更';
        if (value.oldValue == null) changeType = '新增字段';
        if (value.newValue == null) changeType = '删除字段';

        return {
          field: this.mapFieldName(key), // 字段名映射
          oldValue: value.oldValue !== undefined ? formatValue(value.oldValue, key) : null,
          newValue: value.newValue !== undefined ? formatValue(value.newValue, key) : null,
          changeType: changeType
        };
      },

      // 获取每行的明细变更描述
      getDetailDesc(item) {
        const isLineKey = item.startsWith('行号') || item.includes('行号：');
        if (isLineKey) {
          // 明细行处理逻辑
          Object.entries(item).forEach(([key, value]) => {
            return value
          })
        }
        return item;
      },

      // 扁平化嵌套数据结构
      flattenData(obj, prefix = '') {
        return Object.entries(obj).reduce((acc, [key, value]) => {
          const fullKey = prefix ? `${prefix}.${key}` : key
          if (typeof value === 'object' && !Array.isArray(value)) {
            Object.assign(acc, this.flattenData(value, fullKey))
          } else {
            acc[fullKey] = value
          }
          return acc
        }, {})
      },

      // 解析值变更
      parseValueChange(value) {
        const strVal = String(value)
        // 匹配格式：由 [旧值] 变更为 [新值]
        const standardMatch = strVal.match(/由 $$(.*?)$$ 变更为 $$(.*?)$$/)
        if (standardMatch) return [standardMatch[1], standardMatch[2], '标准变更']

        // 匹配新增：新增值 [值]
        const addMatch = strVal.match(/新增值 $$(.*?)$$/)
        if (addMatch) return [null, addMatch[1], '新增字段']

        // 匹配删除：删除值 [值]
        const delMatch = strVal.match(/删除值 $$(.*?)$$/)
        if (delMatch) return [delMatch[1], null, '删除字段']

        // 默认处理
        return [strVal, strVal, '未知变更']
      },

      // 字段名称映射
      mapFieldName(field) {
        const fieldMap = {
          'lineNumber': '行号'
        }
        return fieldMap[field] || this.formatFieldName(field)
      },

      // 格式字段名称（处理驼峰等）
      formatFieldName(name) {
        return name
          .replace(/([A-Z])/g, ' $1')
          .replace(/_/g, ' ')
          .replace(/\./g, ' / ')
          .toLowerCase()
          .replace(/(^|\s)\S/g, l => l.toUpperCase())
      },

      // 解析行号
      parseLineNumber(item) {
        try {
          const json = JSON.parse(item)
          const key = Object.keys(json)[0]
          return key.match(/行号：?(\d+)/)?.[1] || '未知'
        } catch {
          return '未知'
        }
      },

      // 时间格式化
      formatTime(cellValue) {
        // 获取单元格数据
        let dtc = new Date(cellValue)
        //获取月,默认月份从0开始
        let dtuMonth = dtc.getMonth() + 1
        //获取日
        let dtuDay = dtc.getDate()
        //处理1-9月前面加0
        if (dtuMonth < 10) {
          dtuMonth = "0" + (dtc.getMonth() + 1)
        }
        //处理1-9天前面加0
        if (dtuDay < 10) {
          dtuDay = "0" + dtc.getDate()
        }
        //获取小时
        let dtuHours = dtc.getHours()
        //处理1-9时前面加0
        if (dtuHours < 10) {
          dtuHours = "0" + dtc.getHours()
        }
        //获取分钟
        let dtuMinutes = dtc.getMinutes()
        //处理1-9分前面加0
        if (dtuMinutes < 10) {
          dtuMinutes = "0" + dtc.getMinutes()
        }
        //获取秒
        let dtuSeconds = dtc.getSeconds()
        //处理1-9秒前面加0
        if (dtuSeconds < 10) {
          dtuSeconds = "0" + dtc.getSeconds()
        }
        //组装年月日时分秒,按自己的要求来
        return dtc.getFullYear() + "-" + dtuMonth + "-" + dtuDay + " " + dtuHours + ":" + dtuMinutes + ":" + dtuSeconds
      },

      // 处理耗时百分比（示例）
      timePercent() {
        return Math.min(100, Math.floor((this.data.costTime || 0) / 5))
      },

      // 请求方法样式
      methodType() {
        return this.data.requestMethod === 'POST' ? 'success' : ''
      }
    }
  };
</script>

<style scoped>
  .operation-detail-container {
    padding: 0 15px;
  }

  /* .fixed-base-info {
  margin-bottom: 25px;

  :deep(.el-descriptions__body) {
    background: #f8f9fa;
  }
} */
  .fixed-base-info {
    margin-bottom: 25px;
  }

  /* 使用 /deep/ */
  .fixed-base-info /deep/ .el-descriptions__body {
    background: #f8f9fa;
  }

  .change-section {
    margin: 25px 0;
  }

  .section-title {
    display: flex;
    align-items: center;
    color: #606266;
    font-size: 16px;
    margin: 18px 0;
  }

  .el-icon {
    margin-right: 8px;
    font-size: 18px;
  }

  .line-title {
    margin-right: 12px;
    font-weight: 500;
  }

  .highlight-text {
    color: #409eff;
    font-weight: 500;
  }

  .field-name {
    font-weight: 500;
    color: #303133;
  }

  :deep(.el-collapse-item__header) {
    font-weight: 500;
    background: #f8f9fa;
  }

  :deep(.el-progress-bar) {
    padding-right: 70px;
  }
</style>
